package com.googlecode.jvmpart;

import java.io.File;

import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.googlecode.jvmpart.proc.JvmProcess;
import com.googlecode.jvmpart.proc.MainBootstrap;

/**
 * Format the parameters and really start a java process
 * @author chunzhan.he
 *
 */
public class RealStartProcess {
	private static final Log logger = LogFactory.getLog(RealStartProcess.class);

	/**
	 * The form of the parmeters
	 * @param params the paremters to form as jvm process arguments
	 * @return arguments of the jvm process
	 */
	public static String toArguments(Map<String, Object> params) {
		StringBuffer argsBuf = new StringBuffer();
		int count = 0;
		Set<String> keyset = params.keySet();
		String[] keys = keyset.toArray(new String[keyset.size()]);
		for (String key:keys) {
			if (count != 0) {
				argsBuf.append(JvmProcess.PARAMS_DELIMIT);
			}
			argsBuf.append(key);
			argsBuf.append(JvmProcess.PARAMS_SEPERATER);
			String value = null;
			if(key.startsWith(JvmProcess.SENSITIVE_PREFIX))
				value = MainBootstrap.xorString(params.remove(key).toString()
						, ProcessExecution.PID);
			else value = params.get(key).toString();
			argsBuf.append(value);
			count++;
		}
		String args = argsBuf.toString();
		return args;
	}

	/**
	 * To pasing the parameters and really start a jvm process
	 * @param threadExecution The context of the monitor thread, including the parameters,vm arguments...
	 * @return the exit code of the jvm process {}
	 */
	public int execute(ProcessExecution threadExecution) {
		Map<String, Object> params = threadExecution.getParams();
		String clazz = threadExecution.getJvmProcessClass();
		String args = toArguments(params);
		String javaHome = System.getProperty("java.home");
		String javaBin = javaHome + File.separator + "bin" + File.separator
				+ "java";
		String classpath = System.getProperty("java.class.path");
		if(threadExecution.getClasspath()!=null) {
			classpath+=File.pathSeparator+threadExecution.getClasspath();
		}
		threadExecution.setClasspath(classpath);
		List<String> command = new ArrayList<String>();
		command.add(javaBin);
		command.addAll(threadExecution.getVmArguments());
		command.add("-cp");
		command.add(classpath);
		try {
			ProcessBuilder builder = null;
			String mainClazz = MainBootstrap.class.getCanonicalName();
			String encodeParams = URLEncoder.encode(args,
					JvmProcess.PARAMS_ENCODING);
			command.add(mainClazz);
			command.add(clazz);
			command.add(encodeParams);
			command.add(ProcessExecution.PID);
			command.add(Long.toString(ProcessExecution.PID_TIMESTAMP));
			builder = new ProcessBuilder(command);
			logger.debug(builder.command());
			final Process process = builder.start();
			StreamGobbler errorGobbler = new StreamGobbler(
					process.getErrorStream(), "VM-E-" + threadExecution.getName());
			StreamGobbler outputGobbler = new StreamGobbler(
					process.getInputStream(), "VM-I-" + threadExecution.getName());
			errorGobbler.start();
			outputGobbler.start();
//			Runtime.getRuntime().addShutdownHook(new Thread(){
//				public void run() {
//					logger.error("ShutdownHook:"+threadExecution);
//					process.destroy();
//				}
//			});
			process.waitFor();
			return process.exitValue();
		} catch (Throwable t) {
			// t.printStackTrace();
			logger.error(args, t);
		}
		return -1;
	}
}
